﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class CommonInput : Singleton<CommonInput>
{
	public CollidersType m_RaycastType;
	public enum CollidersType
	{
		Colliders_3D,
		Colliders_2D,
		All_Colliders,
	}

	private Ray m_FingerRay;
	private Vector3 m_FingerPoint;
	private RaycastHit m_HitInfo;
	private Collider m_HitCollider = null;
	private Collider2D m_HitCollider2D = null;

	private List<InputEvent> m_Events = new List<InputEvent>();

	#region MONO_METHODS
	private void Update()
	{
		UpdateFingerEvents();
		UpdateFingerTouch();
	}
	#endregion

	public class InputEvent
	{
		public int m_ID;
		public Vector3 m_Position;
		public Vector3 m_PrevPosition;
		public Vector3 m_Delta;
		public Vector3 m_PrevDelta;
		public FingerPhase m_Phase;
		public Collider m_Collider = null;
		public Collider2D m_Collider2D = null;
		public bool m_ChangeDirection;
		public bool m_Stopped;

		public enum FingerPhase
		{
			Pressed,		// Finger is down
			Down,			// Finger start input
			Up,				// Finger ended input
			Dead,			// Finger is released
		}

		public InputEvent(int i_ID)
		{
			m_ID = i_ID;
		}
	}

	#region MULTI TOUCH
	public InputEvent GetEvent(int i_ID)
	{
		foreach (InputEvent evt in m_Events)
		{
			if (evt.m_ID == i_ID)
			{
				return evt;
			}
		}

		return null;
	}

	public List<InputEvent> GetAllEvents()
	{
		return m_Events;
	}

	private void UpdateFingerEvents()
	{
#if (UNITY_IPHONE || UNITY_ANDROID) && !UNITY_EDITOR
		UpdateDeviceInputs();
#else
		UpdateMouseInputs();
#endif
	}

	public void UpdateFingerTouch()
	{
		if (m_Events.Count == 0)	// This return is used for debugging
		{
			return;
		}

		foreach (InputEvent evt in m_Events)
		{
			CalculatePress(evt);
			CalculateDirectionChange(evt);
		}
	}
	#endregion

	#region FINGER METHODS
	private void UpdateFingerInputs()
	{
		foreach (InputEvent evt in m_Events)
		{
			if (evt.m_Phase == InputEvent.FingerPhase.Up)
			{
				evt.m_Phase = InputEvent.FingerPhase.Dead;
			}
		}

		foreach (Touch touch in Input.touches)
		{
			InputEvent evt = GetEvent(touch.fingerId);
			if (evt != null && evt.m_Phase == InputEvent.FingerPhase.Dead)
			{
				m_Events.Remove(evt);
				evt = null;
			}

			if (touch.phase == TouchPhase.Began)
			{
				evt = new InputEvent(touch.fingerId);
				evt.m_Phase = InputEvent.FingerPhase.Down;
				m_Events.Add(evt);
			}
			else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
			{
				evt.m_Phase = InputEvent.FingerPhase.Pressed;
			}
			else if (touch.phase == TouchPhase.Ended)
			{
				evt.m_Phase = InputEvent.FingerPhase.Up;
			}

			if (evt != null)
			{
				evt.m_PrevPosition = evt.m_Position;
				evt.m_Position = touch.position;
				evt.m_PrevDelta = evt.m_Delta;
				evt.m_Delta = touch.deltaPosition;
			}
		}
	}

	private void UpdateMouseInputs()
	{
		bool firstInput = false;
		InputEvent evt = GetEvent(0);
		if (evt != null && evt.m_Phase == InputEvent.FingerPhase.Up)
		{
			m_Events.Remove(evt);
			evt = null;
		}

		if (Input.GetMouseButtonDown(0))
		{
			firstInput = true;
			evt = new InputEvent(0);
			evt.m_Phase = InputEvent.FingerPhase.Down;
			m_Events.Add(evt);
		}
		else if (evt != null && Input.GetMouseButton(0))
		{
			evt.m_Phase = InputEvent.FingerPhase.Pressed;
		}
		else if (evt != null)
		{
			evt.m_Phase = InputEvent.FingerPhase.Up;
		}

		if (evt != null)
		{
			if (firstInput)
			{
				evt.m_PrevPosition = Input.mousePosition;
			}
			else
			{
				evt.m_PrevPosition = evt.m_Position;
			}

			evt.m_Position = Input.mousePosition;
			evt.m_PrevDelta = evt.m_Delta;
			evt.m_Delta = evt.m_Position - evt.m_PrevPosition;
		}
	}

	private void CalculatePress(InputEvent i_Event)
	{
		if (m_RaycastType == CollidersType.Colliders_3D || m_RaycastType == CollidersType.All_Colliders)
		{
			m_FingerRay = Camera.main.ScreenPointToRay(i_Event.m_Position);
			m_HitCollider = null;
			if (Physics.Raycast(m_FingerRay, out m_HitInfo, 1000f))
			{
				m_HitCollider = m_HitInfo.collider;
			}

			if (m_HitCollider != null)
			{
				if (i_Event.m_Phase == InputEvent.FingerPhase.Down)
				{
					i_Event.m_Collider = m_HitCollider;
					m_HitCollider.SendMessage("OnFingerDown", i_Event, SendMessageOptions.DontRequireReceiver);
				}
				else if (i_Event.m_Phase == InputEvent.FingerPhase.Up)
				{
					if (m_HitCollider == i_Event.m_Collider)
					{
						m_HitCollider.SendMessage("OnFingerUpAsButton", i_Event, SendMessageOptions.DontRequireReceiver);
					}

					if (m_HitCollider != null)
					{
						m_HitCollider.SendMessage("OnFingerUp", i_Event, SendMessageOptions.DontRequireReceiver);
					}
				}
			}
		}

		if (m_RaycastType == CollidersType.Colliders_2D || m_RaycastType == CollidersType.All_Colliders)
		{
			m_FingerPoint = Camera.main.ScreenToWorldPoint(i_Event.m_Position);
			m_HitCollider2D = Physics2D.OverlapPoint(m_FingerPoint);

			if (m_HitCollider2D != null)
			{
				if (m_HitCollider2D != null && i_Event.m_Phase == InputEvent.FingerPhase.Down)
				{
					i_Event.m_Collider2D = m_HitCollider2D;
					m_HitCollider2D.SendMessage("OnFingerDown", i_Event, SendMessageOptions.DontRequireReceiver);
				}
				else if (i_Event.m_Phase == InputEvent.FingerPhase.Up)
				{
					if (m_HitCollider2D == i_Event.m_Collider2D)
					{
						m_HitCollider2D.SendMessage("OnFingerUpAsButton", i_Event, SendMessageOptions.DontRequireReceiver);
					}

					if (m_HitCollider2D != null)
					{
						m_HitCollider2D.SendMessage("OnFingerUp", i_Event, SendMessageOptions.DontRequireReceiver);
					}
				}
			}
		}
	}

	private void CalculateDirectionChange(InputEvent i_Event)
	{
		float dot = Vector3.Dot(i_Event.m_Delta, i_Event.m_PrevDelta);

		i_Event.m_ChangeDirection = false;
		if (dot <= 0 && !i_Event.m_Stopped)
		{
			i_Event.m_ChangeDirection = true;
			i_Event.m_Stopped = true;
		}
		else if (dot > 0 && i_Event.m_Stopped)
		{
			i_Event.m_Stopped = false;
		}
	}
	#endregion
}
